package it.ilgrv.demiurgo.control;

import it.ilgrv.demiurgo.persistence.HandleTable;
import it.ilgrv.demiurgo.persistence.tables.character.Character;
import it.ilgrv.demiurgo.persistence.tables.character.CharacterStatusLog;
import it.ilgrv.demiurgo.persistence.tables.character.CharacterStatusType;
import it.ilgrv.demiurgo.persistence.tables.character.CharacterStatusType.CharacterStatusTypeEnum;
import it.ilgrv.demiurgo.persistence.tables.character.Level;
import it.ilgrv.demiurgo.persistence.tables.character.PxLog;
import it.ilgrv.demiurgo.persistence.tables.context.ContextWrapper;
import it.ilgrv.demiurgo.persistence.tables.inscription.User;

import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;

/**
 * this class implements all about PX and character levels.
 * 
 * 
 * @author massimiliano
 * 
 */
public class CharacterLevel extends HandleTable {

	private static String	createCharacterXPLog	= "Xp creazione personaggio";
	private static String	createCharacterStatusLog	= "creazione personaggio";

	public CharacterLevel(EntityManager em) {
		super(em);
	}

	/**
	 * create a new character
	 */
	public Character createNewCharacter(
			User u,
			String name,
			String surname,
			boolean npc,
			String ip
			) {

		Character c = null;

		try {
			beginTransaction();

			c = new Character(name, surname, npc);
			c.setCreationDate(new Date());
			c.setTotalXP(0);
			c.setAvailableXP(0);
			int startingXP = ContextWrapper.getStartingXP();

			em.persist(c);

			c.setCurrentCharacterStatus( em.find(CharacterStatusType.class,
												CharacterStatusTypeEnum.justCreated));
			c =
				changeStatusForCharacter(c, CharacterStatusTypeEnum.justCreated, ip, createCharacterStatusLog);
			c = changeXPForCharacter(c, startingXP, createCharacterXPLog);

			c.setUser(u);
			u.addCharacter(c);
			em.merge(u);
			em.merge(c);

		} finally {
			endTransaction();
		}
		return c;
	}

	/**
	 * sets a new status for the character. Adds a log entry for this change
	 * 
	 * @param c
	 * @param newStatus
	 */
	public Character changeStatusForCharacter(Character c, CharacterStatusTypeEnum newStatus, String ip,
			String note) {
		Character ret = c;
		try {
			beginTransaction();
			CharacterStatusLog csl = new CharacterStatusLog();
			
			CharacterStatusType newStat = em.find(CharacterStatusType.class,
													newStatus);
			
			csl.setDate(new Date());
			csl.setFrom(c.getCurrentCharacterStatus());
			csl.setTo(newStat);
			csl.setIp(ip);
			csl.setNote(note);

			c.setCurrentCharacterStatus(newStat);

		} finally {
			endTransaction();
		}

		return ret;
	}

	/**
	 * modify both totalXP and AvailableXP for character. Adds a log for the
	 * operation
	 * 
	 * @param c
	 *            the managed character
	 * @param xp
	 *            amount of xp to change. if >0 totalXP is also modified
	 * @param description
	 *            motivation of xp change
	 */
	public Character changeXPForCharacter(Character c, int xp, String description) {
		Character ret = c;
		try {
			beginTransaction();

			PxLog pxlog = new PxLog(xp, description);

			pxlog.setCharacter(c);
			c.addPxLogs(pxlog);

			c.setAvailableXP(c.getAvailableXP() + xp);
			if (xp > 0) {
				c.setTotalXP(c.getTotalXP() + xp);
			}

			ret = em.merge(c);
			em.persist(pxlog);

		} finally {
			endTransaction();
		}

		return ret;
	}

	/**
	 * calculate the current level for character
	 * 
	 * @param c
	 * @return the name of the level.
	 */
	public String getCharacterLevel(Character c) {
		if (c == null) {
			return null;
		}
		int xp;
		List<Level> levels = null;
		try {
			beginTransaction();
			xp = c.getTotalXP();

			// getting levels sorted by xp decr
			levels = ContextWrapper.getLevels();

			Iterator<Level> it = levels.iterator();
			Level level;
			while (it.hasNext()) {
				level = it.next();
				if (level.getMinimumXP() < xp) {
					return level.getName();
				}
			}
			return null;

		} finally {
			endTransaction();
		}
	}

	/**
	 * sacrify a character giving a percentage of it's XP to other character. it
	 * follow the formula: obtainedXP = (sacrifiedTotalXP - createCharacterXP )
	 * * percentage / 100
	 * 
	 * @param sacrificed
	 *            the sacrificed character. it cannot be played anymore
	 * @param obtainer
	 *            obtain a percentage of sacrificed character
	 * @return
	 */
	public void sacrifyCharacter(Character sacrificed, Character obtainer, String ip) {
		try {
			beginTransaction();
			int sacrifyPercentage = ContextWrapper.getSacrifyPercentage();
			
			int xp = sacrificed.getTotalXP() - ContextWrapper.getStartingXP();
			xp = xp * sacrifyPercentage / 100;
			
			changeXPForCharacter(obtainer, xp, "sacrificio del personaggio: " + sacrificed.getName() + " "
				+ sacrificed.getSurname());
			changeStatusForCharacter(sacrificed, CharacterStatusTypeEnum.sacrificed, ip,
										"sacrificio personaggio");
		} finally {
			endTransaction();
		}
	}
}
